#include "crc.h"

#include "fragment.h"

#define CRC_G          0x0001CCCC
#define MASK           0x0001FFFF
#define NUMBER_OF_BITS          8

static void
add_fragment_crc (FILE_FRAGMENT * fragment)
{
	guint32	result      = 0;
	guint32 temp        = 0;
	guint   iterator    = 0;
	guint   byte_to_use = 0;
	guint   offset      = 0;

	temp =  (((guint32) fragment->data[0]) << 9) & 0x0001FE00;
	temp |= (((guint32) fragment->data[1]) << 1)  & 0x000001FE;

	if (fragment->data[2] && 0x80)
		temp |= 1;

	for (iterator = 17; iterator < (1024*8); iterator++)
	{
		result = temp ^ (guint32)CRC_G;
	
		temp = ((result << 1) & MASK);
		
		byte_to_use = iterator / NUMBER_OF_BITS;
		offset = iterator % NUMBER_OF_BITS;

		if (fragment->data[byte_to_use] & (1<<offset))
			temp |= 1;
	}

	fragment->crc16 = temp;

	return;
}

static void
add_fragment_group_crc (FILE_FRAGMENT_GROUP * fragment_group)
{
	FILE_FRAGMENT * fragment            = NULL;
	guint           number_of_fragments = 0;
	guint           iterator            = 0;

	number_of_fragments = g_list_length (fragment_group->data_list);

	for (iterator = 0; iterator < number_of_fragments; iterator++)
	{
		fragment = (FILE_FRAGMENT *) g_list_nth_data (fragment_group->data_list, iterator);
		add_fragment_crc (fragment);
	}

	return;
}

void
add_crc (GList * blocks)
{
	FILE_FRAGMENT_GROUP * fragment_group            = NULL;
	guint                 number_of_fragment_groups = 0;
	guint                 iterator                  = 0;

	number_of_fragment_groups = g_list_length (blocks);

	for (iterator = 0; iterator < number_of_fragment_groups; iterator++)
	{
		fragment_group = (FILE_FRAGMENT_GROUP *) g_list_nth_data (blocks, iterator);
		add_fragment_group_crc (fragment_group);
	}

	return;
}

static gboolean
check_fragment_crc (FILE_FRAGMENT * fragment)
{
	guint32	  result         = 0;
	guint32   temp           = 0;
	guint     iterator       = 0;
	guint     byte_to_use    = 0;
	guint     offset         = 0;
	gboolean  is_correct_crc = FALSE;

	temp =  (((guint32) fragment->data[0]) << 9) & 0x0001FE00;
	temp |= (((guint32) fragment->data[1]) << 1)  & 0x000001FE;

	if (fragment->data[2] && 0x80)
		temp |= 1;

	for (iterator = 17; iterator < (1024*8); iterator++)
	{
		result = temp ^ (guint32)CRC_G;
	
		temp = ((result << 1) & MASK);
		
		byte_to_use = iterator / NUMBER_OF_BITS;
		offset = iterator % NUMBER_OF_BITS;

		if (fragment->data[byte_to_use] & (1<<offset))
			temp |= 1;
	}

	if (fragment->crc16 == (temp & 0x0000FFFF))
	{
		is_correct_crc = TRUE;
	}

	return is_correct_crc;
}

static gboolean
check_fragment_group_crc (FILE_FRAGMENT_GROUP * fragment_group)
{
	FILE_FRAGMENT * fragment            = NULL;
	guint           number_of_fragments = 0;
	guint           iterator            = 0;
	gboolean        is_correct_crc      = TRUE;

	number_of_fragments = g_list_length (fragment_group->data_list);

	for (iterator = 0; iterator < number_of_fragments; iterator++)
	{
		fragment = (FILE_FRAGMENT *) g_list_nth_data (fragment_group->data_list, iterator);
		is_correct_crc &= check_fragment_crc (fragment);
	}

	return is_correct_crc;
}

gboolean
check_crc (GList * blocks)
{
	FILE_FRAGMENT_GROUP * fragment_group            = NULL;
	guint                 number_of_fragment_groups = 0;
	guint                 iterator                  = 0;
	gboolean              is_correct_crc            = TRUE;

	number_of_fragment_groups = g_list_length (blocks);

	for (iterator = 0; iterator < number_of_fragment_groups; iterator++)
	{
		fragment_group = (FILE_FRAGMENT_GROUP *) g_list_nth_data (blocks, iterator);
		is_correct_crc &= check_fragment_group_crc (fragment_group);
	}

	return is_correct_crc;
}

